package tools

import (
	"context"
	"encoding/json"
	"io"
	"log"
	"runtime"
	"strings"
	"time"

	"gorm.io/gorm/logger"
)

/*
 +----------------------------------------------------------------------
 + Title        : mysql json log
 + Author       : huwl
 + Version      : 1.0.0
 + Initial-Time : 2021-09-22
 + Last-time    : 这个文件最后修改的时间 + 修改人的名称
 + Desc         : json格式方式输出、记录sql日志方案
 +				: 使用方式:
					// 日志文件路径
					logPath := "logs/gorm.json.log"

					// 确保日志目录存在
					if err := ensureLogDir(logPath); err != nil {
						log.Fatalf("创建日志目录失败: %v", err)
					}

					// 创建/打开日志文件
					logFile, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
					if err != nil {
						log.Fatalf("无法打开日志文件: %v", err)
					}
					defer logFile.Close()

					fmt.Printf("JSON 日志文件已创建: %s\n", logPath)

					// 创建多路日志写入器
					multiWriter := &DualWriter{
						writers: []io.Writer{
							logFile,    // 文件输出
							os.Stdout,  // 控制台输出
						},
					}

					// 创建 JSON 日志记录器
					jsonLogger := &JSONLogger{
						Writer:        multiWriter,
						SlowThreshold: 200 * time.Millisecond,
						LogLevel:      logger.Info,
					}
					// 连接数据库
					db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{
						Logger: jsonLogger,
					})
 +
 +
 +----------------------------------------------------------------------
*/

// JSONLogEntry 定义 JSON 日志结构
type JSONLogEntry struct {
	Timestamp    string  `json:"timestamp"`
	Level        string  `json:"level"`
	Message      string  `json:"message"`
	SQL          string  `json:"sql,omitempty"`
	RowsAffected int64   `json:"rows_affected,omitempty"`
	DurationMs   float64 `json:"duration_ms"`
	SlowQuery    bool    `json:"slow_query,omitempty"`
	SourceFile   string  `json:"source_file,omitempty"`
	SourceLine   int     `json:"source_line,omitempty"`
	Error        string  `json:"error,omitempty"`
}

// JSONLogger 实现 GORM 的 logger.Interface
type JSONLogger struct {
	Writer        io.Writer
	SlowThreshold time.Duration
	LogLevel      logger.LogLevel
}

func (l *JSONLogger) LogMode(level logger.LogLevel) logger.Interface {
	l.LogLevel = level
	return l
}

func (l *JSONLogger) Info(ctx context.Context, msg string, data ...interface{}) {
	if l.LogLevel >= logger.Info {
		l.logEntry("INFO", msg, nil, 0, 0, false)
	}
}

func (l *JSONLogger) Warn(ctx context.Context, msg string, data ...interface{}) {
	if l.LogLevel >= logger.Warn {
		l.logEntry("WARN", msg, nil, 0, 0, false)
	}
}

func (l *JSONLogger) Error(ctx context.Context, msg string, data ...interface{}) {
	if l.LogLevel >= logger.Error {
		l.logEntry("ERROR", msg, nil, 0, 0, false)
	}
}

func (l *JSONLogger) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error) {
	if l.LogLevel <= logger.Silent {
		return
	}

	elapsed := time.Since(begin)
	sql, rows := fc()
	slow := elapsed > l.SlowThreshold && l.SlowThreshold != 0

	var logLevel string
	switch {
	case err != nil && l.LogLevel >= logger.Error:
		logLevel = "ERROR"
	case slow && l.LogLevel >= logger.Warn:
		logLevel = "WARN"
	case l.LogLevel >= logger.Info:
		logLevel = "INFO"
	default:
		return
	}

	l.logEntry(logLevel, "", &sql, rows, elapsed, slow)
}

func (l *JSONLogger) logEntry(level, message string, sql *string, rows int64, duration time.Duration, slow bool) {
	// 获取调用源文件信息
	_, file, line, _ := runtime.Caller(3)
	if idx := strings.LastIndex(file, "/"); idx >= 0 {
		file = file[idx+1:]
	}

	entry := JSONLogEntry{
		Timestamp:  time.Now().Format(time.RFC3339),
		Level:      level,
		Message:    message,
		DurationMs: float64(duration.Microseconds()) / 1000,
		SourceFile: file,
		SourceLine: line,
	}

	if sql != nil {
		entry.SQL = *sql
		entry.RowsAffected = rows
		entry.SlowQuery = slow
	}

	if level == "ERROR" {
		entry.Error = message
	}

	jsonData, err := json.Marshal(entry)
	if err != nil {
		log.Printf("JSON 序列化失败: %v", err)
		return
	}

	jsonData = append(jsonData, '\n') // 添加换行符
	l.Writer.Write(jsonData)
}

// DualWriter 实现 io.Writer 接口，同时写入多个目标
type DualWriter struct {
	writers []io.Writer
}

func (dw *DualWriter) Write(p []byte) (n int, err error) {
	for _, w := range dw.writers {
		n, err = w.Write(p)
		if err != nil {
			return
		}
	}
	return len(p), nil
}
